/* Copyright (C) 2017-2099 All right reserved
 * For free
 *
 * @file DLT645.h
 * @brief  DLT645通信协议实现代码
 * @author qigaohua, qigaohua168@163.com
 * @version v1.0
 * @date 2022-01-05
 */

#ifndef DLT645_HPP_U7ZQWFOL
#define DLT645_HPP_U7ZQWFOL
#include <cstdint>
#include <iostream>
#include <cstring>

// 请求控制码 C
#define DLT645_BROADCAST_JIAOSHI    (0x1E)  // 广播校时, 没有应答
#define DLT645_READ_DATA            (0x11)  // 读数据
#define DLT645_READ_FOLLOW_DATA     (0x12)  // 读后续数据
#define DLT645_READ_COMM_ADDR       (0x13)  // 读通信地址
#define DLT645_WRITE_DATA           (0x14)  // 写后续数据
#define DLT645_WRITE_COMM_ADDR      (0x15)  // 写通信地址
#define DLT645_FREEZE_CMD           (0x16)  // 冻结命令
#define DLT645_MODIFY_COMM_RATE     (0x17)  // 更改通信速率
#define DLT645_MODIFY_PASSWORD      (0x18)  // 修改密码
#define DLT645_MAX_DEMAND_ZERO      (0x19)  // 最大需量清零
#define DLT645_BATTERY_ZERO         (0x1A)  // 电量清零
#define DLT645_EVENT_ZERO           (0x1B)  // 事件清零
#define DLT645_REMOTE_CONTROL       (0x1F)  // 遥控

// 应答控制码 C
#define DLT645_READ_DATA_ACK                           (0x91)
#define DLT645_READ_DATA_HAVE_FOLLOW_ACK               (0xB1)
#define DLT645_READ_DATA_ERROR_ACK                     (0xD1)
#define DLT645_READ_FOLLOW_DATA_ACK                    (0x92)
#define DLT645_READ_FOLLOW_DATA_HAVE_FOLLOW_ACK        (0xB2)
#define DLT645_READ_FOLLOW_DATA_ERROR_ACK              (0xD2)
#define DLT645_WRITE_DATA_ACK                          (0x94)
#define DLT645_WRITE_DATA_ERROR_ACK                    (0xD4)
#define DLT645_READ_COMM_ADDR_ACK                      (0x93)
#define DLT645_WRITE_COMM_ADDR_ACK                     (0x95)
#define DLT645_REMOTE_CONTROL_ACK                      (0x9F)  // 遥控


#define DLT645_READ_DATA_LEN        4
#define DLT645_REMOTE_CONTROL_LEN   9


#define DLT645_FRAME_START_CHAR     0x68
#define DLT645_FRAME_END_CHAR       0x16

#define DLT645_PROTOCOL_NONE        0
#define DLT645_PROTOCOL_1997        1
#define DLT645_PROTOCOL_2007        2

#define DLT645_97_DATA_ID_LEN       2
#define DLT645_07_DATA_ID_LEN       4

#define DLT645_ADDR_LEN             6
#define DLT645_FRAME_DATA_MAX_LEN   256

; // 消除编译警告
#pragma pack(push, 1)
struct DLT645_Frame_t {
    uint8_t st_c1;                           // 帧起始符
    uint8_t addr[DLT645_ADDR_LEN];           // 地址域
    uint8_t st_c2;                           // 帧起始符2
    uint8_t ctrl;                            // 控制码
    uint8_t datalen;                         // 数据长度
    uint8_t data[];                          // 数据  -- 可能包括数据标识+数据+校验码+帧结束符
    // uint8_t cs;
    // uint8_t end_c;
};
#pragma pack(pop)

using DLT645_RequestFrame  = struct DLT645_Frame_t;
using DLT645_ResponseFrame = struct DLT645_Frame_t;


struct DLT645_RespParseFrame_t {
    int8_t   status;         // 0 合法
    uint8_t  protocol;      // 协议
    uint8_t  addr[DLT645_ADDR_LEN];
    uint8_t  ctrl;          // 控制码
    uint8_t  dataid_len;
    uint32_t data_id;       // 数据标识
    uint8_t  data_len;
    uint8_t  data[DLT645_FRAME_DATA_MAX_LEN];
};


class DLT645
{
public:
    enum FrameType_e {
        DLT645_REQUEST_FRAME = 1,
        DLT645_RESPONSE_FRAME,
    };

    enum DLT645_ErrorCode_e {
        DLT645_ERROR_CTRL_CODE  = -6,
        DLT645_NO_VALIAD        = -5,
        DLT645_ERROR_CS         = -4,
        DLT645_ERROR_END_CHAE   = -3,
        DLT645_ERROR_START_CHAE = -2,
        DLT645_OOM              = -1,
        DLT645_OK               = 0,
    };

    DLT645 () {
        // request_frame_size_ = 0;
        // response_frame_size_ = 0;
        // req  = nullptr;
        // resp = nullptr;
        // parse_resp = nullptr;
    };

    DLT645(const uint8_t *addr, uint8_t ctrl,
            uint32_t data_id, uint8_t *data = nullptr, size_t datalen = 0);

    DLT645(uint8_t *flow);

    ~DLT645 ();

    // 构建请求数据域
    bool DLT645_BuildRequestDataField(uint32_t data_id, uint8_t *data, size_t datalen);

    bool DLT645_FramePack(const uint8_t *addr, uint8_t ctrl);
    bool DLT645_FrameUnPack(uint8_t *flow);

    // 获取请求数据流
    uint8_t *DLT645_GetRequestFlow() const {
        return reinterpret_cast<uint8_t *>(req);
    }

    // 获取帧长度
    size_t DLT645_GetRequestFrameLength() const {
        return request_frame_size_;
    }

    // 响应帧信息获取接口
    DLT645_ErrorCode_e DLT645_GetResponseFrameState() const {
        return parse_resp != nullptr ? (DLT645_ErrorCode_e)parse_resp->status : DLT645_NO_VALIAD;
    }

    uint8_t DLT645_GetResponseFrameProtocol() const {
        return parse_resp != nullptr ? parse_resp->protocol : DLT645_PROTOCOL_NONE;
    }

    uint8_t* DLT645_GetResponseFrameAddr() const {
        return parse_resp != nullptr ? parse_resp->addr : nullptr;
    }

    uint8_t DLT645_GetResponseFrameCtrlCode() const {
        return parse_resp != nullptr ? parse_resp->ctrl : 0;
    }

    uint32_t DLT645_GetResponseFrameDataId() const {
        return parse_resp != nullptr ? parse_resp->data_id : 0;
    }

    bool DLT645_GetResponseFrameData(uint8_t **data, uint8_t *datalen) {
        if (parse_resp == nullptr) return false;
        *data = parse_resp->data;
        *datalen = parse_resp->data_len;
        return true;
    }

private:
    // 申请空间
    bool DLT645_AllocFrame(size_t size, FrameType_e type) {
        int8_t *p = new int8_t[size];
        if (nullptr == p) return false;
        if (type == DLT645_REQUEST_FRAME)
            req = reinterpret_cast<DLT645_RequestFrame*>(p);
        else
            parse_resp = reinterpret_cast<DLT645_RespParseFrame_t*>(p);
        return true;
    }

    bool DLT645_VerifyFrame(uint8_t ctrl, uint8_t *data, size_t datalen);

    // DLT645 数据标识与数据域要加上0x33
    void _DLT645_Data_Add_33H(uint8_t *data, size_t len) {
        int i;
        for (i = 0; i < len; i++) data[i] += 0x33;
    }

    void _DLT645_Data_Sub_33H(uint8_t *data, uint8_t len) {
        int i;
        for (i = 0; i < len; i++) data[i] -= 0x33;
    }

    // 计算校验码
    uint8_t DLT645_FrameCS(uint8_t *buf, uint8_t len) {
        uint8_t i, CS = 0;
        // _DLT645_Data_Add_33H(&buf[10], len-10);
        for (i = 0; i < len; i++) CS += buf[i];
        return CS;
    }


    size_t request_frame_size_  = 0;
    DLT645_RequestFrame  *req   = nullptr;
    // DLT645_ResponseFrame *resp  = nullptr;
    DLT645_RespParseFrame_t *parse_resp = nullptr;
};



#endif /* end of include guard: DLT645_HPP_U7ZQWFOL */
